﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Greed
{
    public static class GreedGame
    {
        //Warning! Set console properties to font - Lucida Console 18pt. In that way we will se the exact thing.
        public static int WindowHeight = 30;
        public static int WindowWidth = 80;
        private static int bufferZone = 5;
        private static int matriceHeight = WindowHeight - bufferZone;
        private static int matriceWidth = WindowWidth;
        private static int[,] gameField = new int[matriceHeight, matriceWidth];
        private static int score = 1;
        private static string username;
        private static int currentX;
        private static int currentY;

        static void GenerateGameField()
        {
            Random randomGenerator = new Random();

            for (int row = 0; row < gameField.GetLength(0); row++)
            {
                for (int col = 0; col < gameField.GetLength(1); col++)
                {
                    gameField[row, col] = randomGenerator.Next(1, 10);
                }
            }
        }

        static void SetPlayerFirstPosition()
        {
            //Set on random first position of the '@' - player
            Random ranPlayerCoordes = new Random();

            //Set x and y coordes of the '@' - player
            int xCoordes = ranPlayerCoordes.Next(0, (WindowHeight - bufferZone));
            int yCoordes = ranPlayerCoordes.Next(0, WindowWidth);

            //Set current position
            currentX = xCoordes;
            currentY = yCoordes;

            //Set current position as eaten
            gameField[xCoordes, yCoordes] = 0;

            ////Set player fist position
            //Console.ForegroundColor = ConsoleColor.Red;
            //Console.BackgroundColor = ConsoleColor.White;
            //PrintOnPosition(xCoordes, yCoordes, "@");
            Console.ResetColor();
        }

        #region GameLogic
        static bool IsNotCrossingEatenField(string pressedKey)
        {
            bool isNotCrossing = true;
            int length;

            switch (pressedKey)
            {
                case "A":
                    length = gameField[currentX, currentY - 1];

                    for (int i = 1; i <= length; i++)
                    {
                        if (gameField[currentX, currentY - i] == 0)
                        {
                            isNotCrossing = false;
                            break;
                        }
                    }
                    break;
                case "D":
                    length = gameField[currentX, currentY + 1];

                    for (int i = 1; i <= length; i++)
                    {
                        if (gameField[currentX, currentY + i] == 0)
                        {
                            isNotCrossing = false;
                            break;
                        }
                    }
                    break;
                case "W":
                    length = gameField[currentX - 1, currentY];

                    for (int i = 1; i <= length; i++)
                    {
                        if (gameField[currentX - i, currentY] == 0)
                        {
                            isNotCrossing = false;
                            break;
                        }
                    }
                    break;
                case "X":
                    length = gameField[currentX + 1, currentY];

                    for (int i = 1; i <= length; i++)
                    {
                        if (gameField[currentX + i, currentY] == 0)
                        {
                            isNotCrossing = false;
                            break;
                        }
                    }
                    break;
                case "Q":
                    length = gameField[currentX - 1, currentY - 1];

                    for (int row = 1, col = 1; row <= length; row++, col++)
                    {
                        if (gameField[currentX - row, currentY - col] == 0)
                        {
                            isNotCrossing = false;
                            break;
                        }
                    }
                    break;
                case "E":
                    length = gameField[currentX - 1, currentY + 1];

                    for (int row = 1, col = 1; row <= length; row++, col++)
                    {
                        if (gameField[currentX - row, currentY + col] == 0)
                        {
                            isNotCrossing = false;
                            break;
                        }
                    }
                    break;
                case "Z":
                    length = gameField[currentX + 1, currentY - 1];

                    for (int row = 1, col = 1; row <= length; row++, col++)
                    {
                        if (gameField[currentX + row, currentY - col] == 0)
                        {
                            isNotCrossing = false;
                            break;
                        }
                    }
                    break;
                case "C":
                    length = gameField[currentX + 1, currentY + 1];

                    for (int row = 1, col = 1; row <= length; row++, col++)
                    {
                        if (gameField[currentX + row, currentY + col] == 0)
                        {
                            isNotCrossing = false;
                            break;
                        }
                    }
                    break;
                default:
                    break;
            }

            return isNotCrossing;
        }

        static bool IsValidMovement(string pressedKey)
        {
            bool isValid = false;
            int downFrame = WindowHeight - bufferZone;

            switch (pressedKey)
            {
                case "A":
                    //Check if these steps are not greater then frame size
                    isValid = currentY - 1 >= 0 && (currentY - gameField[currentX, currentY - 1] >= 0) ? true : false;
                    //Check if movement will be greater or equal to the frame && next number is not blank
                    isValid = isValid && gameField[currentX, currentY - 1] != 0;
                    break;
                case "D":
                    isValid = currentY + 1 < WindowWidth && (currentY + gameField[currentX, currentY + 1] < WindowWidth) ? true : false;
                    isValid = isValid && gameField[currentX, currentY + 1] != 0;
                    break;
                case "W":
                    isValid = currentX - 1 >= 0 && (currentX - gameField[currentX - 1, currentY] >= 0) ? true : false;
                    isValid = isValid && gameField[currentX - 1, currentY] != 0;
                    break;
                case "X":
                    isValid = currentX + 1 < downFrame && (currentX + gameField[currentX + 1, currentY] < downFrame) ? true : false;
                    isValid = isValid && gameField[currentX + 1, currentY] != 0;
                    break;
                case "Q":
                    isValid = currentX - 1 >= 0 && currentY - 1 >= 0 ? true : false;
                    isValid = isValid && (currentX - gameField[currentX - 1, currentY - 1] >= 0) && (currentY - gameField[currentX - 1, currentY - 1] >= 0);
                    isValid = isValid && gameField[currentX - 1, currentY - 1] != 0;
                    break;
                case "E":
                    isValid = currentX - 1 >= 0 && currentY + 1 < WindowWidth ? true : false;
                    isValid = isValid && (currentX - gameField[currentX - 1, currentY + 1] >= 0) && (currentY + gameField[currentX - 1, currentY + 1] < WindowWidth);
                    isValid = isValid && gameField[currentX - 1, currentY + 1] != 0;
                    break;
                case "Z":
                    isValid = currentX + 1 < downFrame && currentY - 1 >= 0 ? true : false;
                    isValid = isValid && (currentX + gameField[currentX + 1, currentY - 1] < downFrame) && (currentY - gameField[currentX + 1, currentY - 1] >= 0);
                    isValid = isValid && gameField[currentX + 1, currentY - 1] != 0;
                    break;
                case "C":
                    isValid = currentX + 1 < downFrame && currentY + 1 < WindowWidth ? true : false;
                    isValid = isValid && (currentX + gameField[currentX + 1, currentY + 1] < downFrame) && (currentY + gameField[currentX + 1, currentY + 1] < WindowWidth);
                    isValid = isValid && gameField[currentX + 1, currentY + 1] != 0;
                    break;
                default:
                    break;
            }
            if (isValid)
            {
                isValid = IsNotCrossingEatenField(pressedKey);
            }

            return isValid;
        }

        static bool IsGameOver()
        {
            bool gameOver = true;

            string[] allMoves = { "Z", "C", "A", "X", "D", "Q", "W", "E" };
            bool[] isEveryMoveValidArray = { true, true, true, true, true, true, true, true };

            for (int i = 0; i < allMoves.Length; i++)
            {
                isEveryMoveValidArray[i] = IsValidMovement(allMoves[i]);
            }

            foreach (var move in isEveryMoveValidArray)
            {
                if (move)
                {
                    gameOver = false;
                    break;
                }
            }

            return gameOver;
        }

        static void Move(string pressedKey)
        {
            if (IsValidMovement(pressedKey))
            {
                //Holds number from chosen position
                int length = 0;
                ConsolePrint.PrintOnPosition(currentY, currentX, " ");
                switch (pressedKey)
                {
                    case "A":
                        length = gameField[currentX, currentY - 1];
                        for (int i = 1; i <= length; i++)
                        {
                            gameField[currentX, currentY - i] = 0;
                            ConsolePrint.PrintOnPosition(currentY - i, currentX, " ");
                        }
                        currentY -= length;
                        break;
                    case "D":
                        length = gameField[currentX, currentY + 1];
                        for (int i = 1; i <= length; i++)
                        {
                            gameField[currentX, currentY + i] = 0;
                            ConsolePrint.PrintOnPosition(currentY + i, currentX, " ");
                        }
                        currentY += length;
                        break;
                    case "W":
                        length = gameField[currentX - 1, currentY];
                        for (int i = 1; i <= length; i++)
                        {
                            gameField[currentX - i, currentY] = 0;
                            ConsolePrint.PrintOnPosition(currentY, currentX - i, " ");
                        }
                        currentX -= length;
                        break;
                    case "X":
                        length = gameField[currentX + 1, currentY];
                        for (int i = 1; i <= length; i++)
                        {
                            gameField[currentX + i, currentY] = 0;
                            ConsolePrint.PrintOnPosition(currentY, currentX + i, " ");
                        }
                        currentX += length;
                        break;
                    case "Q":
                        length = gameField[currentX - 1, currentY - 1];
                        for (int row = 1, col = 1; row <= length; row++, col++)
                        {
                            gameField[currentX - row, currentY - col] = 0;
                            ConsolePrint.PrintOnPosition(currentY - col, currentX - row, " ");
                        }
                        currentX -= length;
                        currentY -= length;
                        break;
                    case "E":
                        length = gameField[currentX - 1, currentY + 1];
                        for (int row = 1, col = 1; row <= length; row++, col++)
                        {
                            gameField[currentX - row, currentY + col] = 0;
                            ConsolePrint.PrintOnPosition(currentY + col, currentX - row, " ");
                        }
                        currentX -= length;
                        currentY += length;
                        break;
                    case "Z":
                        length = gameField[currentX + 1, currentY - 1];
                        for (int row = 1, col = 1; row <= length; row++, col++)
                        {
                            gameField[currentX + row, currentY - col] = 0;
                            ConsolePrint.PrintOnPosition(currentY - col, currentX + row, " ");
                        }
                        currentX += length;
                        currentY -= length;
                        break;
                    case "C":
                        length = gameField[currentX + 1, currentY + 1];
                        for (int row = 1, col = 1; row <= length; row++, col++)
                        {
                            gameField[currentX + row, currentY + col] = 0;
                            ConsolePrint.PrintOnPosition(currentY + col, currentX + row, " ");
                        }
                        currentX += length;
                        currentY += length;
                        break;
                    default:
                        break;
                }

                score += length;

                Console.ResetColor();

                PrintInfo();

                Console.SetCursorPosition(currentY, currentX);

            }
            else Console.Beep();
        }
        #endregion

        static void Main()
        {
            Console.WindowHeight = WindowHeight;
            Console.WindowWidth = WindowWidth;
            Console.BufferHeight = Console.WindowHeight;      //Remove scrollbar
            Console.BufferWidth = Console.WindowWidth;        //Remove scrollbar

            GameMenu gameMenu = new GameMenu(WindowWidth, WindowHeight);
            gameMenu.PrintGameMenu();
            GameMenuSelection(gameMenu);

            GenerateGameField();
            PrintGameField();
            SetPlayerFirstPosition();
            PrintInfo();

            bool isPlaying = true;
            while (isPlaying)
            {
                //Set cursor on the start position
                Console.SetCursorPosition(currentY, currentX);

                BlinkCurrentPosition();
                //Read direction - Z C  A S D  Q W E
                ConsoleKeyInfo pressedKey = Console.ReadKey(true);

                if (pressedKey.Key == ConsoleKey.F1)
                {
                    gameMenu.PrintHelp();
                    Console.Clear();
                    PrintGameField();
                    PrintInfo();
                    continue;
                }

                Move(pressedKey.Key.ToString());

                // ToDo: if no more posible moves set isPlaying = false
                // isPlaying = false;
                if (IsGameOver())
                {
                    gameMenu.PrintGameOver();
                    AddUsernameAndUserscore();
                    isPlaying = false;
                }
            }

            Console.WriteLine();
        }

        private static void BlinkCurrentPosition()
        {
            while (!Console.KeyAvailable)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.BackgroundColor = ConsoleColor.White;
                ConsolePrint.PrintOnPosition(currentY, currentX, "@");
                Thread.Sleep(100);
                Console.ForegroundColor = ConsoleColor.White;
                Console.BackgroundColor = ConsoleColor.Red;
                ConsolePrint.PrintOnPosition(currentY, currentX, "@");
                Thread.Sleep(100);
                Console.ResetColor();
            }
        }

        private static void AddUsernameAndUserscore()
        {
            Console.SetCursorPosition(30, 27);
            ConsolePrint.PrintInCenter("Type an username: ", 15);
            username = Console.ReadLine();

            ScoreBoard scoreBoard = new ScoreBoard();

            scoreBoard.AddNewScore(username, score);

            scoreBoard.WriteAllScores();

            scoreBoard.PrintOnConsole();
        }

        private static void GameMenuSelection(GameMenu gameMenu)
        {

            bool isPlayModeSelected = false;
            do
            {
                Console.SetCursorPosition(WindowWidth - 2, WindowHeight - 2);
                var consoleKey = Console.ReadKey();

                switch (consoleKey.Key)
                {
                    case ConsoleKey.DownArrow:
                    case ConsoleKey.S:
                        gameMenu.OnButtonDownPressed();
                        break;
                    case ConsoleKey.UpArrow:
                    case ConsoleKey.W:
                        gameMenu.OnButtonUpPressed();
                        break;
                    case ConsoleKey.Enter:
                        isPlayModeSelected = gameMenu.OnButtonEnterPressed();
                        break;
                    case ConsoleKey.Escape:
                    case ConsoleKey.Backspace:
                        Console.Clear();
                        gameMenu.PrintGameMenu();
                        break;
                    default:
                        break;
                }

            } while (!isPlayModeSelected);

            Console.Clear();
        }

        #region PrintNewGame
        private static void PrintGameField()
        {
            for (int row = 0; row < gameField.GetLength(0); row++)
            {
                for (int col = 0; col < gameField.GetLength(1); col++)
                {
                    Console.ForegroundColor = GetColor(gameField[row, col]);

                    Console.Write(gameField[row, col]);

                    Console.ResetColor();
                }
            }
        }

        private static ConsoleColor GetColor(int number)       //Returns color for every single digit. Everytime the same color.
        {
            switch (number)
            {
                case 0:
                    {
                        return ConsoleColor.Black;
                    }
                case 1:
                    {
                        return ConsoleColor.Blue;
                    }
                case 2:
                    {
                        return ConsoleColor.Cyan;
                    }
                case 3:
                    {
                        return ConsoleColor.Magenta;
                    }
                case 4:
                    {
                        return ConsoleColor.DarkCyan;
                    }
                case 5:
                    {
                        return ConsoleColor.Gray;
                    }
                case 6:
                    {
                        return ConsoleColor.DarkMagenta;
                    }
                case 7:
                    {
                        return ConsoleColor.Green;
                    }
                case 8:
                    {
                        return ConsoleColor.DarkRed;
                    }
                case 9:
                    {
                        return ConsoleColor.Yellow;
                    }
                default:
                    {
                        return ConsoleColor.Gray;
                    }
            }
        }

        private static void PrintInfo()
        {
            double percentage = score * 100.0 / ((WindowHeight - bufferZone) * WindowWidth);       //If someone don't understand this formula contact Aleksandar Dimitrov

            ConsolePrint.PrintOnPosition(2, WindowHeight - 3, string.Format("Score: {0} {1}%", score, percentage));

            ConsolePrint.PrintOnPosition(WindowWidth - 20, WindowHeight - 3, "Press F1 for help.");
        }
        #endregion
    }
}